from datetime import datetime, timedelta
import httplib
import logging
import re
from axis4.service_monitor.models import ServiceCheckHistory
from axis4 import settings
import models

__author__ = 'isd'

class Check(object):
    base_log = logging.getLogger(settings.MAIN_LOGGER)
    def get_code(self):
        return 0

    def perform_check(self, service):
        return ServiceCheckHistory.STATUS_OFFLINE, "Service check failed"

class HttpPing(Check):
    def get_code(self):
        return 1

    def perform_check(self, service):
        m = re.match(ur".*?://(.*?)(/.*)?$", service.url)
        if m is None:
            return ServiceCheckHistory.STATUS_OFFLINE, "Service url incorrect"
        self.base_log.debug("Url: %s Path: %s" % (m.group(1), m.group(2)))
        conn = httplib.HTTPConnection(m.group(1))
        try:
            conn.request("GET", m.group(2) if m.group(2) is not None else "/")
            r = conn.getresponse()
            if r.status == 200:
                return ServiceCheckHistory.STATUS_OK, "OK"
            return ServiceCheckHistory.STATUS_DISRUPTION, "%d %s" % (r.status, r.reason)
        except httplib.NotConnected:
            return ServiceCheckHistory.STATUS_OFFLINE, "Failed to connect"
        except httplib.InvalidURL:
            return ServiceCheckHistory.STATUS_OFFLINE, "Service url incorrect"
        except httplib.UnknownProtocol:
            return ServiceCheckHistory.STATUS_DISRUPTION, "Unknown protocol"
        except httplib.UnknownTransferEncoding:
            return ServiceCheckHistory.STATUS_DISRUPTION, "Unknown transfer encoding"
        except httplib.UnimplementedFileMode:
            return ServiceCheckHistory.STATUS_DISRUPTION, "Unknown file mode"
        except httplib.IncompleteRead:
            return ServiceCheckHistory.STATUS_DISRUPTION, "Incomplete read"
        except httplib.ImproperConnectionState:
            return ServiceCheckHistory.STATUS_DISRUPTION, "Wrong connection state"
        except httplib.BadStatusLine:
            return ServiceCheckHistory.STATUS_DISRUPTION, "Bad status line"
        except Exception, e:
            self.base_log.exception(e)
            return ServiceCheckHistory.STATUS_OFFLINE, "Service check failed. %s" % e.message

def get_checks():
    return [HttpPing()]

class Service(object):
    @classmethod
    def collect_account_services(cls, account):
        return [service_account.service for service_account in models.ServiceAccount.all().filter('account =', account)]

    @classmethod
    def collect_service_checks(cls, service):
        return [service_check for service_check in models.ServiceCheck.all().filter('service =', service)]

    @classmethod
    def collect_service_checks_history(cls, service_check):
        return [service_check_history for service_check_history in models.ServiceCheckHistory.all().filter('service_check =', service_check).filter('when >', datetime.now() + timedelta(hours = -12)).order('-when')]

    @classmethod
    def get_service_check_state(cls, service_check):
        res = cls.collect_service_checks_history(service_check)
        return res[0] if len(res) else models.ServiceCheckHistory(service_check = service_check, status = models.ServiceCheckHistory.STATUS_OFFLINE, status_text = "Service never checked")

    @classmethod
    def get_service_state(cls, service):
        res = cls.collect_service_checks(service)
        if not len(res):
            return None
        worst = None
        for check in res:
            history = cls.get_service_check_state(check)
            if worst is None:
                worst = history
                continue
            if worst.status == models.ServiceCheckHistory.STATUS_OFFLINE:
                continue
            if worst.status == models.ServiceCheckHistory.STATUS_OK:
                worst = history
                continue
            if worst.status == models.ServiceCheckHistory.STATUS_DISRUPTION:
                if history.status == models.ServiceCheckHistory.STATUS_OFFLINE:
                    worst = history
        return worst

    @classmethod
    def get_check_type(cls, type_id):
        return models.CheckType.get_by_id(type_id)

    @classmethod
    def get_service(cls, service_id):
        return models.Service.get_by_id(service_id)

    @classmethod
    def delete_service_check(cls, service_check):
        for history in models.ServiceCheckHistory.all().filter("service_check =", service_check):
            history.delete()
        service_check.delete()

    @classmethod
    def delete_service(cls, service):
        for service_account in models.ServiceAccount.all().filter("service =", service):
            service_account.delete()
        """ full delete version
        for service_check in models.ServiceCheck.all().filter("service =", service):
            cls.delete_service_check(service_check)
        service.delete()"""

    @classmethod
    def is_not_deleted(cls, service):
        return models.ServiceAccount.all().filter("service =", service).count() > 0

    @classmethod
    def update_service_checks(cls, service, check_ids):
        for service_check in models.ServiceCheck.all().filter("service =", service):
            if service_check.key().id() in check_ids:
                check_ids.remove(service_check.key().id())
            else:
                cls.delete_service_check(service_check)
        for check_id in check_ids:
            models.ServiceCheck(service = service, check_type = models.CheckType.get_by_id(check_id)).put()

class Account(object):
    @classmethod
    def get_account(cls, user):
        res = [a for a in models.Account.all().filter('user =', user)]
        if not len(res):
            raise Exception("No such user account")
        return res[0]

    @classmethod
    def check_service(cls, account, service):
        return models.ServiceAccount.all().filter('account =', account).filter('service =', service).count() > 0

class PathUtil(object):
    @classmethod
    def template_directory(cls, exec_file):
        return exec_file[:re.search(r"[/\\]%s[/\\]" % settings.APP_NAME, exec_file).start() + 1] + "templates/"

    @classmethod
    def template_path(cls, exec_file, template_file):
        return "%s%s" % (cls.template_directory(exec_file), template_file)

class ObjectUtil(object):
    @classmethod
    def to_dict(cls, obj):
        return obj.to_dict() if obj is not None and hasattr(obj, "to_dict") else None